`timescale 1ns / 1ps
module ddr2_port(
/**********NAND flash Sync mode Port*********/
    output      WE_s,  
    output      RE_dif,     
    output      CLE,   
    output      ALE, 

    input[7:0]  DQ_i,
    output[7:0] DQ_o, 
    output      DQ_oen, 

    input       DQS_i,
    output      DQS_o,
    output      DQS_oen,
/***********Mode control port**************/
    input       clkX2,
    input       dclk,
    input       rst_n,
    input[2:0]  Cmd,  //
    input[7:0]  InputVal,       //
    output      IV_En,
    output[7:0] ReadVal,
    output      RV_En,
    input[31:0] RWDataNum,
    input[31:0] RWDataCounter
);

parameter tDBS =   16'h0002;
parameter tRPRE =  16'h0008;
parameter tCDQSS = 16'h0008;
parameter tWPRE =  16'h0008;

parameter State_command =   3'b000;
parameter State_adderss =   3'b001;
parameter State_WriteData = 3'b010;
parameter State_ReadData  = 3'b011;
parameter State_Idle =      3'b100;
parameter State_WriteWait = 3'b111;

wire      input_data_en_w;
reg       input_data_en_r;
reg       read_data_vaild;
wire[7:0] read_val;


assign IV_En = input_data_en_w;
assign RV_En =read_data_vaild ;
assign ReadVal = read_val;

wire        clk_m;

wire        we_n_w;
wire        re_n_w;
reg         re_n_val;
reg         cle_r;
reg         ale_r;
reg [7:0]   dq_o_r;
reg         read_en;
reg         wp_n_r;
wire        dqs_i;
wire        dqs_o;
reg         dqs_val;
reg[7:0]    dq_ca;
reg[7:0]    dq_data;

assign WE_s =  we_n_w ;
assign RE_dif = re_n_w ;
assign CLE =  cle_r ;
assign ALE =  ale_r ;

assign DQ_o = dq_o_r;
assign DQ_oen = ~read_en;


assign dqs_i = DQS_i;
assign DQS_o = dqs_o;
assign DQS_oen = ~read_en;

assign clk_m = clkX2;

reg we_clk_en;
reg re_clk_en;
reg dqs_auto_en;
reg re_n_auto_en;
reg ien;

assign we_n_w = we_clk_en ? dclk : 1'b1;
assign re_n_w = re_n_auto_en ? dclk : re_n_val;
assign dqs_o = dqs_auto_en ? dclk : dqs_val;
assign input_data_en_w = (Cmd == State_WriteData | Cmd == State_WriteWait) ? input_data_en_r : ien ? dclk : 1'b0;

always @(*) begin
    if (Cmd == State_WriteData | Cmd == State_WriteWait) begin
        dq_o_r = dq_data;
    end else if(Cmd == State_command | Cmd == State_adderss) begin
        dq_o_r = dq_ca;
    end else if(rst_n == 1) begin
        dq_o_r = dq_o_r;
    end else begin
        dq_o_r = 0;
    end
end

reg[2:0] last_cmd;
always @(posedge dclk or negedge rst_n) begin
    if (!rst_n) begin// 
        ien <= 1'b0;
        last_cmd <= State_Idle;
    end else begin
        if (Cmd == State_Idle & last_cmd == State_Idle | Cmd == State_ReadData) begin
            ien <= 1'b0;
        end else begin
            ien <= 1'b1;
        end
        last_cmd <= Cmd;
    end
end

always @(posedge clk_m or negedge rst_n) begin
    if(!rst_n) begin
        read_en <=  1'b0;
    end else begin
        if (Cmd == State_ReadData) begin
            read_en <= 1'b1;
        end else begin
            read_en <= 1'b0;
        end
    end
end

always @(negedge dclk or negedge rst_n) begin
    if (!rst_n) begin
        cle_r <= 1'b0;
        ale_r <= 1'b0;        
    end else begin
        if (Cmd == State_adderss) begin
            ale_r <= 1'b1;
        end else begin
            ale_r <= 1'b0;
        end
        if (Cmd == State_command && ien) begin
            cle_r <= 1'b1;
        end else begin
            cle_r <= 1'b0;
        end
        if (Cmd == State_Idle) begin
            if (last_cmd == State_WriteData) begin
                cle_r <= cle_r;
                ale_r <= ale_r;
            end else begin
                cle_r <= 1'b1;
                ale_r <= 1'b1;
            end
        end
    end
end

always @(posedge clk_m or negedge rst_n) begin
    if (!rst_n) begin
        we_clk_en <= 1'b0;
    end else begin
        if(Cmd == State_command | Cmd == State_adderss)
            we_clk_en <= 1'b1;
        else 
            we_clk_en <= 1'b0;
    end
end

/***************write cmd or address***********************/

always @(negedge WE_s) begin
    if (Cmd == State_command | Cmd == State_adderss) begin
        dq_ca <= InputVal;
    end else begin
        dq_ca <= 0;
    end
end

/*******************read data****************************/
reg[15:0] re_counter;
reg       re_counter_inc;

always @(posedge clk_m) begin
    if (Cmd == State_ReadData) begin
        if (re_counter == tDBS) begin
            re_n_val <= 1'b0;
            re_n_auto_en <= 1'b0;
        end else if (re_counter == tDBS + tRPRE) begin
            re_n_val <= 1'b1;
            re_n_auto_en <= 1'b1;
            re_counter_inc <= 1'b0;
            re_counter <= re_counter + 1'b1;
        end else if (RWDataCounter == RWDataNum) begin// - 8'h2
            re_n_val <= 1'b0;
            re_n_auto_en <= 1'b0;
        end else if (RWDataCounter > RWDataNum) begin//=
            re_n_val <= 1'b0;
            re_n_auto_en <= 1'b0;
        end else begin
            re_n_val <= re_n_val;
            re_n_auto_en <= re_n_auto_en;
        end
        re_counter <= re_counter + re_counter_inc;
    end else begin
        re_counter <= 16'h0000;
        re_n_val <= 1'b1;
        re_n_auto_en <= 1'b0;
        re_counter_inc <= 1'b1;
    end
end



reg[7:0] read_valp;
reg[7:0] read_valn;
reg dpc;
reg dnc;
wire bp;
wire bn;

assign read_val = (dpc ^~ dnc) ? read_valp : read_valn;
assign bp = dqs_i == 1 && Cmd == State_ReadData;
assign bn = dqs_i == 0 && Cmd == State_ReadData;

always @(posedge dqs_i or negedge rst_n) begin     
    if (!rst_n) begin
        read_data_vaild <= 1'b0;
        dpc <= 0;
        read_valp <= 0;
    end else begin
        if (bp) begin
            read_data_vaild <= 1'b1;
            read_valp <= DQ_i;   
            dpc <= ~dpc;
        end else begin
            read_data_vaild <= read_data_vaild;
            dpc <= dpc;
            read_valp <= read_valp;
        end
    end
end

always @(negedge dqs_i or negedge rst_n) begin
    if (!rst_n) begin
        dnc <= 0;
        read_valn <= 0;
    end else begin
        if (bn) begin
            read_valn <= DQ_i;
            dnc <= ~dnc; 
        end else begin
            dnc <= dnc;
            read_valn <= read_valn;
        end
    end
end

/*******************write data****************************/

reg[15:0] dqs_counter;
reg       dqs_counter_inc;
always @(posedge clk_m) begin
    if (Cmd == State_WriteData || Cmd == State_WriteWait) begin
        if (Cmd == State_WriteData) begin
            if (dqs_counter < tCDQSS) begin
                dqs_auto_en <= 1'b0;
                dqs_val <= 1'b1;
            end else if (dqs_counter == tCDQSS + tWPRE - 8'h2)begin
                input_data_en_r <= 1'b1;
            end else if (dqs_counter < tCDQSS + tWPRE) begin
                dqs_auto_en <= 1'b0;
                dqs_val <= 1'b0;
            end else begin
                if (dclk == 1'b0) begin
                    if(input_data_en_r)
                        dqs_auto_en <= 1'b1;
                    input_data_en_r <= 1'b1;
                end else begin
                    dqs_auto_en <= dqs_auto_en;
                    dqs_val <= dqs_val;
                    dqs_counter_inc <= 1'b0;
                end
            end
            dqs_counter <= dqs_counter + dqs_counter_inc;
        end else begin
           dqs_auto_en <= 1'b0;
           dqs_val <= 1'b0;   
           input_data_en_r <= 1'b0;
        end
    end else begin
        dqs_counter_inc <= 1'b1;
        dqs_auto_en <= 1'b0;
        dqs_val <= 1'b0;
        input_data_en_r <= 1'b0;
        dqs_counter <= 16'h0000;
    end
end

always @(negedge clk_m) begin
    if(Cmd == State_WriteWait)begin
        dq_data <= dq_data;
    end else if(Cmd == State_WriteData & input_data_en_r) begin
        dq_data <= InputVal;
    end else begin 
        dq_data <= dq_data;
    end
end

endmodule
